#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/if.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_ether.h>
#include <linux/netfilter.h>		/* for NF_ACCEPT */
#include <errno.h>
#include <libnetfilter_log/libnetfilter_log.h>
#include <sys/time.h>
#define NIPQUAD(addr) \
    ((unsigned char *)&addr)[0], \
    ((unsigned char *)&addr)[1], \
    ((unsigned char *)&addr)[2], \
    ((unsigned char *)&addr)[3]

static int print_pkt(struct nflog_data *ldata)
{
    int ret;
	char *payload;
	struct nfulnl_msg_packet_hdr *ph;
    uint32_t seq, global_seq;
    struct timeval tv;

    if(nflog_get_timestamp(ldata, &tv) != 0) {
        printf("nflog_get_timestamp failed.\n");
    }
    if(nflog_get_seq(ldata, &seq) == 0) {
        printf("seq = %d\n", seq);
    }
    else {
        fprintf(stderr, "failed seq\n");
    }
    if(nflog_get_seq(ldata, &global_seq) == 0) {
        printf("global_seq = %d\n", global_seq);
    }
    else {
        fprintf(stderr, "failed global_seq\n");
    }
    
    ph = nflog_get_msg_packet_hdr(ldata);
	if(ph) {
        if(ntohs(ph->hw_protocol) != ETH_P_IP) {
            goto this_exit;
        }
	}

	ret = nflog_get_payload(ldata, &payload);
	if(ret > 0) {
        struct iphdr *iph = (struct iphdr *)payload;
        if((iph->protocol != IPPROTO_TCP) && 
           (iph->protocol != IPPROTO_UDP) && 
           (iph->protocol != IPPROTO_ICMP)) {
            //printf("We Only Suport UDP,TCP,ICMP\n");
            goto this_exit;
        }

        //printf("%ld.%ld %u.%u.%u.%u --> %u.%u.%u.%u\n", 
        //        tv.tv_sec, tv.tv_usec, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
#if 0
        struct tbr_monitor_task *task = (struct tbr_monitor_task *)calloc(1, sizeof(*task));
        if(NULL == task) {
            debug_err("%s: calloc(), %s\n", __func__, strerror(errno));
            goto this_exit;
        }
        snprintf(task->addr, sizeof(task->addr) - 1,
                 "%u.%u.%u.%u", NIPQUAD(iph->saddr));
        tbr_monitor_task_in(task);
#endif
    }


this_exit:
	return 0;
}

static int cb(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg,
		struct nflog_data *nfa, void *data)
{
	print_pkt(nfa);
	return NF_ACCEPT;
}


int main(int argc, char **argv)
{
	struct nflog_handle *h;
	struct nflog_g_handle *qh100;
	struct nflog_g_handle *qh101;
	int rv, fd;
	char buf[4096];
    system("iptables -D INPUT -i ens33 -p udp -s 10.10.10.110 --sport 12345 -j NFLOG --nflog-group 100 --nflog-threshold 1");
    system("iptables -A INPUT -i ens33 -p udp -s 10.10.10.110 --sport 12345 -j NFLOG --nflog-group 100 --nflog-threshold 1");
    system("iptables -D INPUT -i ens33 -p udp -s 10.10.10.110 --sport 12346 -j NFLOG --nflog-group 101 --nflog-threshold 1");
    system("iptables -A INPUT -i ens33 -p udp -s 10.10.10.110 --sport 12346 -j NFLOG --nflog-group 101 --nflog-threshold 1");

	h = nflog_open();
	if (!h) {
		fprintf(stderr, "error during nflog_open()\n");
		exit(1);
	}
    printf("nflog_open ok\n");

	if (nflog_unbind_pf(h, AF_INET) < 0) {
		fprintf(stderr, "error nflog_unbind_pf()\n");
		exit(1);
	}
	printf("unbinding existing nf_log handler for AF_INET (if any)\n");

	if (nflog_bind_pf(h, AF_INET) < 0) {
		fprintf(stderr, "error during nflog_bind_pf()\n");
		exit(1);
	}
	printf("binding nfnetlink_log to AF_INET\n");

	qh100 = nflog_bind_group(h, 100);
	if (!qh100) {
		fprintf(stderr, "no handle for group 100\n");
		exit(1);
	}
	printf("binding this socket to group 100\n");

    nflog_set_flags(qh100, NFULNL_CFG_F_SEQ);
    nflog_set_flags(qh100, NFULNL_CFG_F_SEQ_GLOBAL);
	if (nflog_set_mode(qh100, NFULNL_COPY_PACKET, 0xffff) < 0) {
		fprintf(stderr, "can't set packet copy mode\n");
		exit(1);
	}
	printf("setting copy_packet mode\n");

	qh101 = nflog_bind_group(h, 101);
	if (!qh101) {
		fprintf(stderr, "no handle for group 100\n");
		exit(1);
	}
	printf("binding this socket to group 100\n");

    nflog_set_flags(qh101, NFULNL_CFG_F_SEQ);
    nflog_set_flags(qh101, NFULNL_CFG_F_SEQ_GLOBAL);
	if (nflog_set_mode(qh101, NFULNL_COPY_PACKET, 0xffff) < 0) {
		fprintf(stderr, "can't set packet copy mode\n");
		exit(1);
	}
	printf("setting copy_packet mode\n");

	fd = nflog_fd(h);

	nflog_callback_register(qh100, &cb, NULL);
	nflog_callback_register(qh101, &cb, NULL);
	printf("registering callback for group 100\n");

	printf("going into main loop\n");
	while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
		//printf("pkt received (len=%u)\n", rv);

		/* handle messages in just-received packet */
		nflog_handle_packet(h, buf, rv);
	}

	printf("unbinding from group 100\n");
	nflog_unbind_group(qh100);
	nflog_unbind_group(qh101);

#ifdef INSANE
	/* norally, applications SHOULD NOT issue this command,
	 * since it detaches other programs/sockets from AF_INET, too ! */
	printf("unbinding from AF_INET\n");
	nflog_unbind_pf(h, AF_INET);
#endif

	printf("closing handle\n");
	nflog_close(h);

	return EXIT_SUCCESS;
}

